Next | Prev | Up | Top | Contents | Index

New Functionality for Working With Textures

OpenGL 1.1 provides several new ways of working with textures:

Improving Performance With Predefined Texture Formats

When you create a texture using OpenGL 1.0, you can only define it as having 1, 2, 3, or 4 components. OpenGL 1.1 provides several additional predefined formats, which offer these advantages:

To use the new texture formats, call glTexImage1D() or glTexImage2D(), specifying one of the newly-defined tokens as the components parameter. Table 2-1 lists some of the formats defined in OpenGL 1.1; for a complete list, see the reference page for glTexImage*().

Table 2-1 shows the token names with their corresponding base formats and desired component resolutions. It also shows red (R), green (G), blue (B), luminance (L), alpha (A), and intensity (I) values for each format. You can query the actual resolution with glGetTexLevelParameteriv() using the appropriate token, such as GL_TEXTURE_RED_SIZE. All OpenGL implementations accept all tokens, but an implementation doesn't always allocate a resolution exactly equal to that in the table.

Some Internal Formats Supported in OpenGL 1.1
Token NameBase FormatRGBALI
GL_LUMINANCE8GL_LUMINANCE    8 
GL_LUMINANCE4_ALPHA4GL_LUMINANCE_ALPHA    44 
GL_LUMINANCE12_ALPHA4 GL_LUMINANCE_ALPHA   412 
GL_LUMINANCE16_ALPHA16 GL_LUMINANCE_ALPHA   1616 
GL_INTENSITY8GL_INTENSITY     8
GL_RGB4GL_RGB4 4 4   
GL_RGB8GL_RGB888   
GL_RGBA4GL_RGBA4444  
GL_RGB5_A1GL_RGBA5551  
GL_RGBA8GL_RGBA8888  

New Ways of Using Texture Environments

The section "Modulating and Blending" in the OpenGL Programming Guide explains how you can use a texture map. You can use it to:

OpenGL 1.1 expands on that functionality by providing a new texture environment, GL_REPLACE. In addition, you can use two of the new texture formats, GL_ALPHA and GL_INTENSITY, in conjunction with any of the available texture formats.

Table 2-2 illustrates how the texture functions use the different texture formats with the different environments (Replace, Modulate, Blend, and Decal). The abbreviations have the following meanings:

Rv, Gv, Bv, AvResult of the texture environment function
Rt, Gt, Bt, AtTexture color
Rf, Gf, Bf, AfFragment color
Rc, Gc, Bc, AcTexture environment color (see GL_TEXTURE_ENV_COLOR)

Texture Functions
Base Texture FormatReplaceModulateBlendDecal
GL_
LUMINANCE
Rv = Lt

Gv = Lt

Bv = Lt

Av = Af

Rv = Rf * Lt

Gv = Gf * Lt

Bv = Bf * Lt

Av = Af

Rv = Rf * (1-Lt) + Rc* Lt

Gv = Gf * (1-Lt) + Gc * Lt

Bv = Bf * (1-Lt) + Bc * Lt

Av = Af

undefined
GL_ALPHARv = Rf

Gv = Gf

Bv = Gf

Av = At

Rv = Rf

Gv = Gf

Bv = Gf

Av = Af * At

Rv = Rf

Gv = Gf

Bv = Bf

Av = Af * At

undefined
GL_
INTENSITY
Rv = It

Gv = It

Bv = It

Av = It

Rv = Rf * It

Gv = Gf * It

Bv = Bf * It

Av = Af * It

Rv = Rf * (1-It) + Rc * It

Gv = Gf * (1-It) + Gc * It

Bv = Bf * (1-It) + Bc * It

Av = Af * (1-It) + AcIt

undefined
GL_
LUMINANCE_ALPHA
Rv = Lt

Gv = Lt

Bv = Lt

Av = At

Rv = Rf * Lt

Gv = Gf * Lt

Bv = Bf * Lt

Av = Af * At

Rv = Rf * (1-Lt) + Rc * Lt

Gv = Gf * (1-Lt) + Gc * Lt

Bv = Bf * (1-Lt) + Bc * Lt

Av = Af * At

undefined
GL_RGBRv = Rt

Gv = Gt

Bv = Bt

Av = Af

Rv = Rf * Rt

Gv = Gf * Gt

Bv = Bf * Bt

Av = Af

Rv = Rf * (1-Rt) + Rc * Rt

Gv = Gf * (1-Gt) + Gc * Gt

Bv = Bf * (1-Bt) + Bc * Bt

Av = Af

Rv = Rt

Gv = Gt

Bv = Bt

Av = Af

GL_RBGARv = Rt

Gv = Gt

Bv = Bt

Av = At

Rv = Rf * Rt

Gv = Gf * Gt

Bv = Bf * Bt

Av = Af * At

Rv = Rf * (1-Rt) + Rc * Rt

Gv = Gf * (1-Gt) + Gc * Gt

Bv = Bf * (1-Bt) + Bc * Bt

Av = Af * At

Rv = Rf * (1-At) + Rt * At

Gv = Gf * (1-At) + Gt * At

Bv = Bf * (1-At) + Bt * At

Av = Af

Testing Whether Textures Fit: The Texture Proxy Mechanism

In OpenGL 1.0, it's difficult to determine whether a texture fits into texture memory on a certain system. When you call glGetIntegerv() with the parameter GL_MAX_TEXTURE_SIZE, you can't be sure of an accurate result: Texel format, component resolution, and the shape of a texture determine whether the texture fits. To ensure that a texture of any format and resolution fits, the function returns a result based on the worst-case scenario.

A solution to this problem is the proxy mechanism offered in OpenGL 1.1. To test whether a certain texture fits, follow these steps:

  1. Call glTexImage2D() or glTexImage1D() with target set to GL_PROXY_TEXTURE_2D or GL_PROXY_TEXTURE_1D (see Example 2-1).

  2. Test the proxy texture state values to see if the texture would fit and how it would be stored in texture memory by calling glGetTexLevelParameteriv() or glGetTexLevelParameterfv(). Set target to one of the proxy textures: GL_PROXY_TEXTURE_2D or GL_PROXY_TEXTURE_1D. Set pname to the parameter you are interested in, for example, GL_TEXTURE_WIDTH. If the call returns a non-zero value, the texture will fit.

    If the texture is too large, all of the proxy state variables are set to zero. If the texture can be accommodated, these values are set to the requested parameters. The proxy 1D texture behaves like the proxy 2D texture; however, its state does not include GL_TEXTURE_HEIGHT.

    You still must call glTexImage2D() with target GL_TEXTURE_2D to define the actual texture.

  3. To determine the maximum array size for a mipmap texture, specify and query the proxy texture at the highest level that accurately reflects the aspect ratio of the desired level zero array.

Example 2-1 provides a code fragment that determines the largest texture that can be allocated for a given internal image format and aspect ratio. The program incrementally selects larger textures to find the maximum that will fit.

Example 2-1 : Using the Proxy Mechanism

/*
** Demonstrates use of the proxy texture target to probe texture space 
** to determine the largest texture which can be allocated for a given 
** internal image format and aspect ratio. 
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <GL/gl.h>
#include <GL/glu.h>

/* (int)floor(log2(x)) */
static int
intFloorLog2(unsigned int x)
{
    int a = 0;
    while (x >>= 1) ++a;
    return a;
}

/* true if x is a power of two */
static GLboolean
isPow2(unsigned int x)
{
    return ((x > 0) && (x & (x - 1) == 0));
}

/* find largest texture with specified internal format and aspect */
static void
findMaxTexture(GLenum internalFormat, int dim,
    int xAspect, int yAspect, int border, int maxMipmapLevel,
    int *widthOut, int *heightOut)
{
    int level = 0, levelOffset = 0;
    int width = 1, height = 1;
    int maxLevel = 0, maxWidth = 0, maxHeight = 0;

    if (xAspect > yAspect) {
        width = xAspect / yAspect;
        levelOffset = intFloorLog2(width);
    } else {
        height = yAspect / xAspect;
        levelOffset = intFloorLog2(height);
    }

    while (1) {
        GLint proxyComponents;

        switch (dim) {
          case 1:
            glTexImage1D(GL_PROXY_TEXTURE_1D,
                         level, internalFormat,
                         width+2*border, border,
                         GL_RGBA, GL_UNSIGNED_BYTE, NULL);
            
            glGetTexLevelParameteriv(GL_PROXY_TEXTURE_1D, level,
                    GL_TEXTURE_COMPONENTS, &proxyComponents);
            break;
          case 2:
            glTexImage2D(GL_PROXY_TEXTURE_2D,
                     level, internalFormat,
                     width+2*border, height+2*border, border,
                     GL_RGBA, GL_UNSIGNED_BYTE, NULL);
               
            glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D,level
                     GL_TEXTURE_COMPONENTS,&proxyComponents);
            break;
          default:
            *widthout = 0; 
            *heightout = 0;
            return;
        }
        
        if (proxyComponents != internalFormat) {
           /* proxy allocation failed -- we're done */
            break;
        } else {
           /* proxy allocation succeeded -- see how we did */
               if (level>maxLevel || width>maxWidth || 
                                                    height>maxHeight)
            {
                maxLevel = level;
                maxWidth = width << level;
                maxHeight = height << level;
            }
        }

        if (maxMipmapLevel == 0) {
       /* try the next larger image size at level zero */
            width <<= 1;
            height <<= 1;
        } else {
       /* try same image size at next higher mipmap level */
            ++level;
            if (level+levelOffset > maxMipmapLevel) {
       /* can't query levels which don't exist--we're done */
                break;
            }
        }
    }

    if (maxWidth>0 && maxHeight>0) {
        maxWidth += 2*border;
        maxHeight += 2*border;
    }
    *widthOut = maxWidth;
    *heightOut = maxHeight;
}
static void
displayTextureSizeInfo(void)
{
    GLint maxTextureSize, maxTextureLevel;
    int maxWidth, maxHeight;

    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
    printf("GL_MAX_TEXTURE_SIZE: %d\n", maxTextureSize);

    maxTextureLevel = intFloorLog2(maxTextureSize);

    findMaxTexture(GL_RGB, 2, 1, 1, 0, 0, &maxWidth, &maxHeight);
    printf("RGB 1:1 (%d x %d)\n", maxWidth, maxHeight);

    findMaxTexture(GL_RGBA, 2, 2, 1, 0, 0, &maxWidth, &maxHeight);
    printf("RGBA 2:1 (%d x %d)\n", maxWidth, maxHeight);

    findMaxTexture(GL_RGB, 2, 1, 1, 0, maxTextureLevel, &maxWidth, 
                                                        &maxHeight);
    printf("RGB 1:1 (%d x %d) mipmap\n", maxWidth, maxHeight);

    findMaxTexture(GL_LUMINANCE_ALPHA, 2, 1, 1, 1, 0, &maxWidth, 
                                                      &maxHeight);
    printf("LUMINANCE_ALPHA 1:1 (%d x %d) w/border\n", maxWidth,
                                                       maxHeight);
}

Improving Performance With Texture Objects

Texture objects can improve the performance of programs that use multiple textures. You can create as many textures as you need and switch from one texture to another efficiently. A new texture parameter, the priority, can be used to tell the system which textures should have priority when it allocates hardware resources.

This section first provides some background information in How Texture Objects Work. You then learn about Using Texture Objects in general terms, and about two special topics, Texture Object Names and Editing and Querying Texture Objects. Texture Priorities and Residency explains how you can indicate which textures should have preference when hardware resources are allocated. Finally, Default Textures explains how you can access OpenGL 1.0 textures.

How Texture Objects Work

In OpenGL 1.0, only one texture can be associated with each of the texture targets GL_TEXTURE_1D and GL_TEXTURE_2D. If you want to render a scene that contains many 2D textures, you have to redefine the 2D texture, calling glTexImage2D() for each texture in the scene. If you want each texture to have different parameters, such as wrap modes and filters, you have to redefine those as well.

In OpenGL 1.1, you can create as many textures as you need. You associate a name (a positive number) with a texture object when you create it, and you define the images and parameters of the texture. As you render your scene, you bind the name of each desired texture object to the appropriate texture target. Since binding a texture takes less time than defining one, this is an efficient way to switch from one texture to another.

Note: Texture objects and display lists are in different name spaces. However, if two contexts are sharing display lists, they also share texture objects.

Using Texture Objects

To create a texture object, bind an unused name to a texture target. The name is considered to be in use until you delete the texture. The following code fragment creates, defines, and binds texture objects with the names 1 and 2:

Example 2-2 : Using Texture Objects

/* the first bind of 1 will create the texture object */
glBindTexture( GL_TEXTURE_2D, 1 );
/* define the texture image and parameterss */
glTexImage2D( GL_TEXTURE_2D, 0, 4, 32, 32, 0, GL_RGBA, GL_BYTE, img1 );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );

/* the first bind of 2 will create the texture object */
glBindTexture( GL_TEXTURE_2D, 2 );
/* define the texture image and parameters */
glTexImage2D( GL_TEXTURE_2D, 0, 4, 64, 64, 0, GL_RGBA, GL_BYTE, img2 );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );

/* bind each texture object as scene is rendered */
glBindTexture( GL_TEXTURE_2D, 1 );
<draw some primitive>
glBindTexture( GL_TEXTURE_2D, 2 );
<draw some primitive>

There is also an example program in the source tree that demonstrates how to use texture objects.

Texture Object Names

You can create a texture object with any name that isn't already the name of another texture object. To find out whether a particular name is in use, call glIsTexture(). As a convenience, glGenTextures() returns a set of texture names that are known to be unused.

To delete one or more texture objects, call glDeleteTextures(). After a texture object is deleted, its name is freed; it has no contents, and its resources are returned to the system.

Editing and Querying Texture Objects

Once a texture object has been created, you can change its images and parameters at any time. You edit a texture object the same way you define it: bind the texture object to a target and then call glTexImage*() or glTexParameter*(). These commands take a texture target as an argument, and they affect the texture object that is currently bound to that target. Similarly, you can query the parameters and images of a texture by calling glGetTexParameter*(), glGetTexLevelParameter*(), and glGetTexImage(), which also operate on the currently bound texture.

To find out which texture object is currently bound to each texture target, call glGetIntegerv() with GL_TEXTURE_BINDING_1D or GL_TEXTURE_BINDING_2D.

Texture Priorities and Residency

On some systems, textures must reside in hardware resources such as texture memory before they can be used. When you bind a texture, the system may need to swap out some other textures to make room, if the application's textures do not all fit in texture memory.

An application can guide the system in determining which textures should remain resident by specifying a priority for each texture using glPrioritizeTextures(). Each priority value is clamped to the range 0 to 1, with 0 indicating the lowest priority (and the least likelihood of being resident), and 1 indicating the highest priority. You can also set the priority of a single texture by calling glTexParameter*() with GL_TEXTURE_PRIORITY. Note that the performance effects, if any, of setting texture priorities are entirely system dependent.

You can query whether a set of texture objects is resident by calling glAreTexturesResident(), and you can query whether a single texture object is resident by calling glGetTexParameter*() with GL_TEXTURE_RESIDENT.

Default Textures

You can still access the OpenGL 1.0 textures, now called the "default textures," by binding 0 to a texture target. This is in fact the initial binding when you create a rendering context. When you create a texture object, its images and parameters are the same as the initial settings of the default textures.

You can set the priority of a default texture by calling glTexParameter*(); you cannot use glPrioritizeTextures() with a default texture. Similarly, you can query the residence of a default texture with glGetTexParameter*(), but you cannot do so with glAreTexturesResident().

Updating Textures Quickly With Subtextures

OpenGL 1.1 lets you replace a subrectangle of an existing texture image without affecting the remaining portions of the image. Using a subtexture is the fastest way to update only the image of a texture. When you intend to redefine only the image of the texture and keep all other parameters, using subtexture is much faster than a glTexImage*() call.

Subtextures are especially useful under these circumstances:

In this section, you learn about Using Subtextures and Using Null Images With Subtextures. New and Extended Functions provides information about new functions and functions that changed in OpenGL 1.1 to provide subtexture functionality.

Using Subtextures

To load a subrectangle of a texture image, call glTexSubImage1D(), glTexSubImage2D() (prototype below) or glTexSubImage3D().

void glTexSubImage2D (enum target, int level, int xoffset,int yoffset, 

sizei width, sizei height, enum format, enum type, const void* pixels)

Using any uninitialized portion of a texture while drawing yields undefined results.

Using Null Images With Subtextures

It is sometimes useful to define the parameters of a texture image without actually initializing the contents of that image. For example, if you want to load a frame of an NTSC video as a texture, you need to first define a region larger than the video frame that meets the height and width requirements of textures (power of 2). Null images are also useful when you want to first define the parameters of a texture and later initialize the texture image using glTexSubImage*() calls.

To create a null image, call one of the texture creation functions, for example, glTexImage2D() with pixels set to the null pointer. The specified texture is created, but no pixels are processed. Using an uninitialized part of a texture yields undefined results.

The following code fragment creates a null image texture, then loads a subimage:

{
/* need to initialize "image" to something interesting */
    static unsigned char image[32][32][4]; 
/* create a 256 x 256 null texture */
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB5, 256, 256, 0,
                 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
/* load a 32 x 32 subimage starting at 100,110 */
    glTexSubImage2D(GL_TEXTURE_2D, 0, 100, 110, 32, 32,
                       GL_RGBA, GL_UNSIGNED_BYTE, image);
} 

Loading Textures From the Framebuffer

In OpenGL 1.0, image data for a texture comes from host memory. In OpenGL 1.1, you can load texture images directly from the framebuffer. You can replace either part of the texture image or the whole image.

Using this functionality, you can do multi-pass rendering, using the full set of OpenGL rendering capabilities to generate a texture you can use for further drawing. For example, you can render a scene, copy the resulting image to texture memory, and then apply the texture to a triangle mesh. By changing the mesh, you can warp the scene, break it into pieces, wrap it around complex objects, and so on.

Copying Texture Images

The functions glCopyTexImage1D() and glCopyTexImage2D() copy image data from the color buffer specified by the current read buffer rather than accepting image data from host memory.

To copy an image from the framebuffer, call glCopyTexImage2D(), glCopyTexImage1D(), glCopyTexSubImage1D(), or glCopyTexSubImage2D(). The prototypes for the four functions are very similar; the prototype of glCopyTexImage2D() is provided below as an example:

void glCopyTexImage2D ( GLenum target,GLint level,GLenum internalformat,
           GLint x, GLint y, GLsizei width, GLsizei height, GLint border)

The pixel values are processed exactly as if glCopyPixels() had been called, but the process stops just before final conversion, that is, before clamping and conversion to fragments. At this point all pixel component values are clamped to [0,1], and then treated exactly as if the corresponding glTexImage*() or glTexSubImage*() had been called. Pixel ordering is such that lower x screen coordinates correspond to lower i (horizontal) texture coordinates, and lower y screen coordinates correspond to lower j (vertical) texture coordinates.

New and Extended Functions

For additional information about new texture functionality, see the reference pages for the following functions, which are either new in OpenGL1.1 or have been modified significantly because of additions and changes:

New: glGenTextures(), glDeleteTextures(), glIsTexture(), glBindTexture() glPrioritizeTextures(), glAreTexturesResident(), glTexSubImage1D(), glTexSubImage2D(), glTexSubImage2D().glCopyTexImage1D(), glCopyTexImage2D(), glCopyTexSubImage1D(), glCopyTexSubImage2D(), glCopyTexSubImage3D().

Extended: glTexImage1D(), glTexImage2D(), glGetTexLevelParameteriv(), glTexEnvf(), glTexEnvi(), glTexEnvfv(), glTexEnviv(), glGetTexLevelParameterfv(), glGetParameterTex*().


Improving Performance With Predefined Texture Formats
New Ways of Using Texture Environments
Testing Whether Textures Fit: The Texture Proxy Mechanism
Improving Performance With Texture Objects
How Texture Objects Work
Using Texture Objects
Texture Object Names
Editing and Querying Texture Objects
Texture Priorities and Residency
Default Textures
Updating Textures Quickly With Subtextures
Using Subtextures
Using Null Images With Subtextures
Loading Textures From the Framebuffer
Copying Texture Images
New and Extended Functions

Next | Prev | Up | Top | Contents | Index